using System;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;

namespace Dida.Test
{
	public struct ParamValue
	{
		internal string name;
		internal object value;

		public string Name
		{
			get { return name; }
		}

		public object Value
		{
			get { return value; }
		}
	}

	/// <summary>
	/// This class provides information of the current state of the
	/// unit testing process, up to the level of a specific <see cref = "Runner"/>.
	/// </summary>
	/// <remarks>
	/// Once this class is passed to a <see cref = "ITestResultListener"/>, it is
	/// immutable, and so it's safe to keep a reference to this class, and use the
	/// information later on.
	/// </remarks>
	public class RunnerStateInfo
	{
		internal UnitTestingSettings settings;
		internal UnitTest unitTest;
		internal Runner runner;

		public UnitTestingSettings Settings
		{
			get { return settings; }
		}

		public UnitTest UnitTest
		{
			get { return unitTest; }
		}

		public Runner Runner
		{
			get { return runner; }
		}

		internal void CopyRunnerStateInfo(RunnerStateInfo src)
		{
			settings = src.settings;
			unitTest = src.unitTest;
			runner = src.runner;
		}
	}

	/// <summary>
	/// This class provides information of the current state of the
	/// unit testing process, up to the level of a specific iteration.
	/// </summary>
	/// <remarks>
	/// Once this class is passed to a <see cref = "ITestResultListener"/>, it is
	/// immutable, and so it's safe to keep a reference to this class, and use the
	/// information later on.
	/// </remarks>
	public class IterationStateInfo : RunnerStateInfo
	{
		internal int iterationIndex;
		internal ParamValue[] paramValues;

		public int IterationIndex
		{
			get { return iterationIndex; }
		}

		public IList<ParamValue> ParamValues
		{
			get { return Array.AsReadOnly<ParamValue>(paramValues); }
		}

		internal void CopyIterationStateInfo(IterationStateInfo src)
		{
			iterationIndex = src.iterationIndex;
			paramValues = src.paramValues;

			CopyRunnerStateInfo(src);
		}
	}

	/// <summary>
	/// This class provides the result of a particular iteration of a particular method,
	/// and it also contains all the information about the state the testing process
	/// was in when the method was called.
	/// </summary>
	/// <remarks>
	/// Once this class is passed to a <see cref = "ITestResultListener"/>, it is
	/// immutable, and so it's safe to keep a reference to this class, and use the
	/// information later on.
	/// </remarks>
	public class TestMethodResult : IterationStateInfo
	{
		internal bool succeeded;
		internal MethodInfo methodInfo;
		internal string message;

		/// <summary>
		/// Gets whether the test succeeded.
		/// </summary>
		public bool Succeeded
		{
			get { return succeeded; }
		}

		/// <summary>
		/// Gets the <see cref = "MethodInfo"/> of the test method.
		/// </summary>
		public MethodInfo MethodInfo
		{
			get { return methodInfo; }
		}

		/// <summary>
		/// Gets a message that describes why the test failed. This property
		/// is only valid when the <see cref = "Succeeded"/> property is false.
		/// </summary>
		/// <remarks>
		/// If the reason for a failure was an assert (see the <see cref = "Assert"/> class),
		/// the message will be the message passed to the assert function that was used.
		/// If if failed because of an unhandled exception, the <see cref = "Exception.Message"/>
		/// value will be used.
		/// </remarks>
		public string Message
		{
			get
			{
				Debug.Assert(!succeeded, "Only test methods that failed have a message associated with them.");
				return message;
			}
		}
	}

	public interface ITestResultListener
	{
		void Begin(ProgressSegment fullProgressSegment);
		void End();

		void BeginTest(UnitTest test, ProgressSegment testProgressSegment);
		void EndTest(UnitTest test);

		void BeginRunner(RunnerStateInfo stateInfo);
		void EndRunner(RunnerStateInfo stateInfo);

		void BeginIteration(IterationStateInfo stateInfo);
		void EndIteration(IterationStateInfo stateInfo);

		void ReportSuccess(TestMethodResult result);
		void ReportFailure(TestMethodResult result);
	}

	public class TestResultListenerWrapper
	{
		ITestResultListener listener;

		UnitTest curTest;
		RunnerStateInfo curRunnerStateInfo;
		IterationStateInfo curIterationStateInfo;

		internal TestResultListenerWrapper(ITestResultListener listener)
		{
			this.listener = listener;
		}

		public void Begin(ProgressSegment fullProgressSeg)
		{
			listener.Begin(fullProgressSeg);
		}

		public void End()
		{
			listener.End();
		}

		public void BeginTest(UnitTest test,ProgressSegment testProgressSegment)
		{
			listener.BeginTest(test,testProgressSegment);

			curTest = test;
		}

		public void EndTest()
		{
			listener.EndTest(curTest);
		}

		public void BeginRunner(RunnerStateInfo stateInfo)
		{
			listener.BeginRunner(stateInfo);
			curRunnerStateInfo = stateInfo;
		}

		public void EndRunner()
		{
			listener.EndRunner(curRunnerStateInfo);
		}

		public void BeginIteration(IterationStateInfo stateInfo)
		{
			curIterationStateInfo = stateInfo;
			listener.BeginIteration(stateInfo);
		}

		public void EndIteration()
		{
			listener.EndIteration(curIterationStateInfo);
		}

		public void ReportSuccess(TestMethodResult method)
		{
			listener.ReportSuccess(method);
		}

		public void ReportFailure(TestMethodResult method)
		{
			listener.ReportFailure(method);
		}

		public ITestResultListener TestResultListener
		{
			get { return listener; }
		}
	}
}
